/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "i2c.h"
#include "memorymap.h"
#include "octospi.h"
#include "usart.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bmm150.h"
#include "flash.h"
#include "oled.h"
#include "oledfont.h"
#include "ospi_w25q64.h"
#include "stdio.h"
#include <stdint.h>

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
/* USER CODE BEGIN PFP */
static int8_t OSPI_W25Qxx_Test(void); // W25Q64 Flash 内存映射读写测试函数
static int8_t OSPI_W25Qxx_Test2(void); // W25Q64 Flash HAL读写测试函数

static void STM32_FLASH_Test(void); // STM32 Flash读写测试函数
static void OLED_Test(void); // OLED测试函数
static void BMM150_Test(void); // BMM150测试函数
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
// uint8_t largeBuffer[0x2000ULL] __attribute__((section(".axi_sram_buffer"))); // 用于测试大数据量传输

/* USER CODE END 0 */

/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void)
{

    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MPU Configuration--------------------------------------------------------*/
    MPU_Config();

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_I2C5_Init();
    MX_OCTOSPI1_Init();
    /* USER CODE BEGIN 2 */
    printf("Welcome use STM32H723ZGT6\r\n");

    // ================= OSPI_TEST ==============================
    OSPI_W25Qxx_Init();
    // OSPI_W25Qxx_Test();
    OSPI_W25Qxx_Test2();
    // ================= OSPI_TEST ==============================

    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */

    while (1) {
        // a = HAL_GetTick();
        // for (int i = 0; i < 100; i++) {
        //     my_delay_us(100);
        // }
        // // HAL_Delay(10);
        // b = HAL_GetTick();
        // OLED_ShowNum(0, 4, a, 10, 16);
        // OLED_ShowNum(0, 6, b, 10, 16);

        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        HAL_GPIO_TogglePin(Led_GPIO_Port, Led_Pin);
        HAL_Delay(499);
    }
    /* USER CODE END 3 */
}

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
    RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };

    /** Supply configuration update enable
     */
    HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

    /** Configure the main internal regulator output voltage
     */
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

    while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) { }

    /** Initializes the RCC Oscillators according to the specified parameters
     * in the RCC_OscInitTypeDef structure.
     */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 5;
    RCC_OscInitStruct.PLL.PLLN = 110;
    RCC_OscInitStruct.PLL.PLLP = 1;
    RCC_OscInitStruct.PLL.PLLQ = 2;
    RCC_OscInitStruct.PLL.PLLR = 2;
    RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
    RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    RCC_OscInitStruct.PLL.PLLFRACN = 0;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler();
    }

    /** Initializes the CPU, AHB and APB buses clocks
     */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
        | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2
        | RCC_CLOCKTYPE_D3PCLK1 | RCC_CLOCKTYPE_D1PCLK1;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) {
        Error_Handler();
    }
}

/* USER CODE BEGIN 4 */
void delay_us(uint32_t us)
{
    __IO uint32_t currentTicks = SysTick->VAL;
    /* Number of ticks per millisecond */
    const uint32_t tickPerMs = SysTick->LOAD + 1;
    /* Number of ticks to count */
    const uint32_t nbTicks = ((us - ((us > 0) ? 1 : 0)) * tickPerMs) / 1000;
    /* Number of elapsed ticks */
    uint32_t elapsedTicks = 0;
    __IO uint32_t oldTicks = currentTicks;
    do {
        currentTicks = SysTick->VAL;
        elapsedTicks += (oldTicks < currentTicks) ? tickPerMs + oldTicks - currentTicks : oldTicks - currentTicks;
        oldTicks = currentTicks;
    } while (nbTicks > elapsedTicks);
}

void my_delay_us(uint32_t us)
{
    __IO uint32_t startTicks = SysTick->VAL;
    const uint32_t tickPerMs = SysTick->LOAD + 1;
    const uint32_t nbTicks = (us * tickPerMs) / 1000;

    while (1) {
        __IO uint32_t currentTicks = SysTick->VAL;
        uint32_t elapsedTicks = (startTicks >= currentTicks)
            ? (startTicks - currentTicks)
            : (startTicks + tickPerMs - currentTicks);
        if (elapsedTicks >= nbTicks) {
            break;
        }
    }
}

static void STM32_FLASH_Test(void)
{
    uint32_t data_write[10] = { 1111, 1222, 555555, 14, 5, 66666666, 77777777, 8, 19, 110 };

    // ================= FLASH_TEST ==============================
    printf("Write Flash Start...\r\n");
    if (HAL_OK == STM32Flash_Write(FLASH_USER_START_ADDR, (uint8_t*)data_write, 40)) {
        printf("Write Flash Success!\r\n");
    } else {
        printf("Write Flash Failed!\r\n");
    }

    uint32_t data_read[10] = { 0 };
    if (HAL_OK == STM32Flash_Read(FLASH_USER_START_ADDR, (uint8_t*)data_read, 40)) {
        printf("Read Flash Success!\r\n");
        for (int i = 0; i < 10; i++) {
            printf("data_read[%d] = %ld\r\n", i, data_read[i]);
        }
    } else {
        printf("Read Flash Failed!\r\n");
    }
    // ================= FLASH_TEST ==============================
}

static void OLED_Test(void)
{
    uint32_t a, b;
    // ================= OLED_TEST ==============================
    OLED_Init();
    OLED_Clear();
    // OLED_ShowString(0, 0, (uint8_t*)"STM32H723ZGT6", 16);
    // OLED_ShowNum(0, 2, SystemCoreClock / 1000, 6, 16);
    OLED_ShowNum(0, 0, SysTick->LOAD, 8, 16);
    a = HAL_GetTick();
    for (int i = 0; i < 1000; i++) {
        delay_us(100);
    }
    b = HAL_GetTick();
    // OLED_ShowNum(10, 0, a, 5, 16);
    // OLED_ShowNum(10, 2, 5, 5, 16);
    OLED_Display_On();

    // ================= OLED_TEST ==============================
}

static void BMM150_Test(void)
{
    // ================= BMM150_TEST ==============================
    int16_t x, y, z;
    BMM150_Init();
    OLED_Clear();
    OLED_Display_On();
    uint8_t chipid = BMM150_ReadChipID();
    OLED_ShowNum(0, 0, chipid, 2, 16);

    uint8_t rslt = BMM150_MagDataGet(&x, &y, &z);
    if (rslt == HAL_OK) {
        OLED_ShowNum(0, 4, x, 6, 16);
        OLED_ShowNum(8, 4, y, 6, 16);
        OLED_ShowNum(0, 6, z, 6, 16);
    } else if (rslt == HAL_ERROR) {
        OLED_ShowString(0, 2, (uint8_t*)"Error", 16);
    } else {
        OLED_ShowNum(0, 6, rslt, 3, 16);
    }
    // ================= BMM150_TEST ==============================
}

/***************************************************************************************************
 *	函 数 名: OSPI_W25Qxx_Test
 *	入口参数: 无
 *	返 回 值: OSPI_W25Qxx_OK - 测试成功并通过
 *	函数功能: 进行简单的读写测试，并计算速度
 *	说    明: 无
 ***************************************************************************************************/
#define W25Qxx_NumByteToTest 32 * 1024 // 测试数据的长度，32K
int32_t OSPI_Status; // 检测标志位
uint8_t W25Qxx_WriteBuffer[W25Qxx_NumByteToTest] __attribute__((section(".axi_sram_buffer"))); //	写数据数组
uint8_t W25Qxx_ReadBuffer[W25Qxx_NumByteToTest] __attribute__((section(".axi_sram_buffer"))); //	读数据数组

static int8_t OSPI_W25Qxx_Test(void) // Flash读写测试
{
    /********************************************** 变量定义 *******************************************/


    uint32_t i = 0X8000; // 计数变量
    uint32_t W25Qxx_TestAddr = 0; // 测试地址
    uint32_t ExecutionTime_Begin; // 开始时间
    uint32_t ExecutionTime_End; // 结束时间
    uint32_t ExecutionTime; // 执行时间
    float ExecutionSpeed; // 执行速度

    // 擦除 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    ExecutionTime_Begin = HAL_GetTick(); // 获取 systick 当前时间，单位ms
    OSPI_Status = OSPI_W25Qxx_BlockErase_32K(W25Qxx_TestAddr); // 擦除32K字节
    ExecutionTime_End = HAL_GetTick(); // 获取 systick 当前时间，单位ms

    ExecutionTime = ExecutionTime_End - ExecutionTime_Begin; // 计算擦除时间，单位ms

    if (OSPI_Status == OSPI_W25Qxx_OK) {
        printf("\r\nW25Q64 擦除成功, 擦除32K字节所需时间: %d ms\r\n", ExecutionTime);
    } else {
        printf("\r\n 擦除失败!!!!!  错误代码:%d\r\n", OSPI_Status);
        while (1)
            ;
    }

    // 写入 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    for (i = 0; i < W25Qxx_NumByteToTest; i++) // 先将数据写入数组
    {
        W25Qxx_WriteBuffer[i] = i;
    }
    ExecutionTime_Begin = HAL_GetTick(); // 获取 systick 当前时间，单位ms
    OSPI_Status = OSPI_W25Qxx_WriteBuffer(W25Qxx_WriteBuffer, W25Qxx_TestAddr, W25Qxx_NumByteToTest); // 写入数据
    ExecutionTime_End = HAL_GetTick(); // 获取 systick 当前时间，单位ms

    ExecutionTime = ExecutionTime_End - ExecutionTime_Begin; // 计算擦除时间，单位ms
    ExecutionSpeed = (float)W25Qxx_NumByteToTest / ExecutionTime; // 计算写入速度，单位 KB/S
    if (OSPI_Status == OSPI_W25Qxx_OK) {
        printf("\r\n写入成功,数据大小：%d KB, 耗时: %d ms, 写入速度：%.2f KB/s\r\n", W25Qxx_NumByteToTest / 1024, ExecutionTime, ExecutionSpeed);
    } else {
        printf("\r\n写入错误!!!!!  错误代码:%d\r\n", OSPI_Status);
        while (1)
            ;
    }

    // 读取	>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    OSPI_Status = OSPI_W25Qxx_MemoryMappedMode(); // 配置OSPI为内存映射模式
    if (OSPI_Status == OSPI_W25Qxx_OK) {
        printf("\r\n进入内存映射模式成功，开始读取>>>>\r\n");
    } else {
        printf("\r\n内存映射错误！！  错误代码:%d\r\n", OSPI_Status);
        while (1)
            ;
    }

    ExecutionTime_Begin = HAL_GetTick(); // 获取 systick 当前时间，单位ms
    memcpy(W25Qxx_ReadBuffer, (uint8_t*)W25Qxx_Mem_Addr + W25Qxx_TestAddr, W25Qxx_NumByteToTest); // 从 QSPI_Mem_Addr +W25Qxx_TestAddr 地址处，拷贝数据到 W25Qxx_ReadBuffer
    ExecutionTime_End = HAL_GetTick(); // 获取 systick 当前时间，单位ms

    ExecutionTime = ExecutionTime_End - ExecutionTime_Begin; // 计算擦除时间，单位ms
    ExecutionSpeed = (float)W25Qxx_NumByteToTest / 1024 / 1024 / ExecutionTime * 1000; // 计算读取速度，单位 MB/S

    if (OSPI_Status == OSPI_W25Qxx_OK) {
        printf("\r\n读取成功,数据大小：%d KB, 耗时: %d ms, 读取速度：%.2f MB/s \r\n", W25Qxx_NumByteToTest / 1024, ExecutionTime, ExecutionSpeed);
    } else {
        printf("\r\n读取错误!!!!!  错误代码:%d\r\n", OSPI_Status);
        while (1)
            ;
    }
    // 数据校验 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    for (i = 0; i < W25Qxx_NumByteToTest; i++) // 验证读出的数据是否等于写入的数据
    {
        //		printf("input[%4d] = %8x output[%4d] = %8x\r\n",i,W25Qxx_WriteBuffer[i],i,W25Qxx_ReadBuffer[i]);
        if (W25Qxx_WriteBuffer[i] != W25Qxx_ReadBuffer[i]) // 如果数据不相等，则返回0
        {
            printf("\r\n数据校验失败!!!!!出错位置：%d\r\n", i);
            while (1)
                ;
        } else {
        }
    }
    printf("\r\n校验通过!!!!! QSPI驱动W25Q64测试正常\r\n");

    // 读取整片Flash的数据，用以测试速度 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    printf("\r\n*****************************************************************************************************\r\n");
    printf("\r\n上面的测试中，读取的数据比较小，耗时很短，加之测量的最小单位为ms，计算出的读取速度误差较大\r\n");
    printf("\r\n接下来读取整片flash的数据用以测试速度，这样得出的速度误差比较小\r\n");
    printf("\r\n开始读取>>>>\r\n");
    ExecutionTime_Begin = HAL_GetTick(); // 获取 systick 当前时间，单位ms

    for (i = 0; i < W25Qxx_FlashSize / (W25Qxx_NumByteToTest); i++) // 每次读取 W25Qxx_NumByteToTest 字节的数据
    {
        memcpy(W25Qxx_ReadBuffer, (uint8_t*)W25Qxx_Mem_Addr + W25Qxx_TestAddr, W25Qxx_NumByteToTest); // 从 QSPI_Mem_Addr 地址处，拷贝数据到 W25Qxx_ReadBuffer
        W25Qxx_TestAddr = W25Qxx_TestAddr + W25Qxx_NumByteToTest;
    }
    ExecutionTime_End = HAL_GetTick(); // 获取 systick 当前时间，单位ms

    ExecutionTime = ExecutionTime_End - ExecutionTime_Begin; // 计算擦除时间，单位ms
    ExecutionSpeed = (float)W25Qxx_FlashSize / 1024 / 1024 / ExecutionTime * 1000; // 计算读取速度，单位 MB/S

    if (OSPI_Status == OSPI_W25Qxx_OK) {
        printf("\r\n读取成功,数据大小：%d MB, 耗时: %d ms, 读取速度：%.2f MB/s \r\n", W25Qxx_FlashSize / 1024 / 1024, ExecutionTime, ExecutionSpeed);
    } else {
        printf("\r\n读取错误!!!!!  错误代码:%d\r\n", OSPI_Status);
        while (1)
            ;
    }

    return OSPI_W25Qxx_OK; // 测试通过
}

static int8_t OSPI_W25Qxx_Test2(void)		//Flash HAL读写测试
{
	uint32_t i = 0X8000;	// 计数变量
	uint32_t W25Qxx_TestAddr  =	0	;							// 测试地址	
	uint32_t ExecutionTime_Begin;		// 开始时间
	uint32_t ExecutionTime_End;		// 结束时间
	uint32_t ExecutionTime;				// 执行时间	
	float    ExecutionSpeed;			// 执行速度

// 擦除 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   
	
	
	ExecutionTime_Begin 	= HAL_GetTick();	// 获取 systick 当前时间，单位ms
	OSPI_Status 			= OSPI_W25Qxx_BlockErase_32K(W25Qxx_TestAddr);	// 擦除32K字节
	ExecutionTime_End		= HAL_GetTick();	// 获取 systick 当前时间，单位ms
	
	ExecutionTime = ExecutionTime_End - ExecutionTime_Begin; // 计算擦除时间，单位ms
	
	if( OSPI_Status == OSPI_W25Qxx_OK )
	{
		printf ("\r\nW25Q64 擦除成功, 擦除32K字节所需时间: %d ms\r\n",ExecutionTime);		
	}
	else
	{
		printf ("\r\n 擦除失败!!!!!  错误代码:%d\r\n",OSPI_Status);
		while (1);
	}	
	
// 写入 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

	for(i=0;i<W25Qxx_NumByteToTest;i++)  //先将数据写入数组
	{
		W25Qxx_WriteBuffer[i] = i;
	}
	ExecutionTime_Begin 	= HAL_GetTick();	// 获取 systick 当前时间，单位ms
	OSPI_Status				= OSPI_W25Qxx_WriteBuffer(W25Qxx_WriteBuffer,W25Qxx_TestAddr,W25Qxx_NumByteToTest); // 写入数据
	ExecutionTime_End		= HAL_GetTick();	// 获取 systick 当前时间，单位ms
	
	ExecutionTime  = ExecutionTime_End - ExecutionTime_Begin; 		// 计算擦除时间，单位ms
	ExecutionSpeed = (float)W25Qxx_NumByteToTest / ExecutionTime ; // 计算写入速度，单位 KB/S
	if( OSPI_Status == OSPI_W25Qxx_OK )
	{
		printf ("\r\n写入成功,数据大小：%d KB, 耗时: %d ms, 写入速度：%.2f KB/s\r\n",W25Qxx_NumByteToTest/1024,ExecutionTime,ExecutionSpeed);		
	}
	else
	{
		printf ("\r\n写入错误!!!!!  错误代码:%d\r\n",OSPI_Status);
		while (1);
	}	
	
// 读取	>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 

	
	ExecutionTime_Begin 	= HAL_GetTick();	// 获取 systick 当前时间，单位ms	
	OSPI_Status				= OSPI_W25Qxx_ReadBuffer(W25Qxx_ReadBuffer,W25Qxx_TestAddr,W25Qxx_NumByteToTest);	// 读取数据
	ExecutionTime_End		= HAL_GetTick();	// 获取 systick 当前时间，单位ms
	
	ExecutionTime  = ExecutionTime_End - ExecutionTime_Begin; 					// 计算擦除时间，单位ms
	ExecutionSpeed = (float)W25Qxx_NumByteToTest/1024/1024 / ExecutionTime*1000 ; 	// 计算读取速度，单位 MB/S 
	
	if( OSPI_Status == OSPI_W25Qxx_OK )
	{
		printf ("\r\n读取成功,数据大小：%d KB, 耗时: %d ms, 读取速度：%.2f MB/s \r\n",W25Qxx_NumByteToTest/1024,ExecutionTime,ExecutionSpeed);		
	}
	else
	{
		printf ("\r\n读取错误!!!!!  错误代码:%d\r\n",OSPI_Status);
		while (1);
	}			
// 数据校验 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   
	
	for(i=0;i<W25Qxx_NumByteToTest;i++)	//验证读出的数据是否等于写入的数据
	{
		if( W25Qxx_WriteBuffer[i] != W25Qxx_ReadBuffer[i] )	//如果数据不相等，则返回0	
		{
			printf ("\r\n数据校验失败!!!!!出错位置：%d\r\n",i);	
			while(1);
		}
	}			
	printf ("\r\n校验通过!!!!! QSPI驱动W25Q64测试正常\r\n");		
	
// 读取整片Flash的数据，用以测试速度 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	
	printf ("\r\n*****************************************************************************************************\r\n");		
	printf ("\r\n上面的测试中，读取的数据比较小，耗时很短，加之测量的最小单位为ms，计算出的读取速度误差较大\r\n");		
	printf ("\r\n接下来读取整片flash的数据用以测试速度，这样得出的速度误差比较小\r\n");		
	printf ("\r\n开始读取>>>>\r\n");		
	ExecutionTime_Begin 	= HAL_GetTick();	// 获取 systick 当前时间，单位ms		
	
	for(i=0;i<W25Qxx_FlashSize/(W25Qxx_NumByteToTest);i++)	// 每次读取 W25Qxx_NumByteToTest 字节的数据
	{
		OSPI_Status				= OSPI_W25Qxx_ReadBuffer(W25Qxx_ReadBuffer,W25Qxx_TestAddr,W25Qxx_NumByteToTest);	// 读取数据
		W25Qxx_TestAddr = W25Qxx_TestAddr + W25Qxx_NumByteToTest;		
	}
	ExecutionTime_End		= HAL_GetTick();	// 获取 systick 当前时间，单位ms
	
	ExecutionTime  = ExecutionTime_End - ExecutionTime_Begin; 								// 计算擦除时间，单位ms
	ExecutionSpeed = (float)W25Qxx_FlashSize/1024/1024 / ExecutionTime*1000  ; 	// 计算读取速度，单位 MB/S 

	if( OSPI_Status == OSPI_W25Qxx_OK )
	{
		printf ("\r\n读取成功,数据大小：%d MB, 耗时: %d ms, 读取速度：%.2f MB/s \r\n",W25Qxx_FlashSize/1024/1024,ExecutionTime,ExecutionSpeed);		
	}
	else
	{
		printf ("\r\n读取错误!!!!!  错误代码:%d\r\n",OSPI_Status);
		while (1);
	}	
	
	return OSPI_W25Qxx_OK ;  // 测试通过				
	
}

/* USER CODE END 4 */

/* MPU Configuration */

void MPU_Config(void)
{
    MPU_Region_InitTypeDef MPU_InitStruct = { 0 };

    /* Disables the MPU */
    HAL_MPU_Disable();

    /** Initializes and configures the Region and the memory to be protected
     */
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.Number = MPU_REGION_NUMBER0;
    MPU_InitStruct.BaseAddress = 0x0;
    MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
    MPU_InitStruct.SubRegionDisable = 0x87;
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
    MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
    MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
    MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    /* Enables the MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1) {
    }
    /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
 * @brief  Reports the name of the source file and the source line number
 *         where the assert_param error has occurred.
 * @param  file: pointer to the source file name
 * @param  line: assert_param error line source number
 * @retval None
 */
void assert_failed(uint8_t* file, uint32_t line)
{
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
       ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
